package hydrap2p.gui;

import hydrap2p.gui.commands.Cmd_FilterLabelPanel_AddNewLabel;
import hydrap2p.gui.commands.Cmd_FilterLabelPanel_DeleteLabel;
import hydrap2p.gui.commands.CommandInvoker;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * A panel that displays the current labels and lets the user determine the download listing filtering by
 * selecting an arbitrary number of labels.
 * 
 * @author Jared
 */
public class FilterLabelPanel extends JScrollPane {

	DefaultListModel labelModel;
	private JList labelList;
	private ArrayList<String> labels;

	//the popup menu and its listener
	private JPopupMenu contextMenu = new LabelPopupMenu();
	private ActionListener contextListener = new MenuItemListener();

	private static volatile FilterLabelPanel instance;

	/**
	 * This method ensures the FilterLabelPanel is a Singleton object.  
	 * If this object does not exist when the method is called, it will be constructed and the reference will be returned to the invoker.  
	 * If this object does exist when the method is called, a reference to it will be returned to the invoker.  
	 * @return A reference to the single FilterLabelPanel object
	 */
	public static synchronized FilterLabelPanel getInstance() {
		if (instance == null) { // If this object has not been created yet...
			synchronized (FilterLabelPanel.class) { // Check to see if the FilterLabelPanel class is being accessed
				if (instance == null) { // If the class is not being accessed and this object still doesn't exist...
					instance = new FilterLabelPanel(); // Create a new instance of the FilterLabelPanel
				}
			}
		}
		return instance; // Return a reference to the FilterLabelPanel object
	}

	/**
	 * This method constructs a new FilterLabelPanel object and calls init() to initialize all of the necessary variables.
	 */
	private FilterLabelPanel() {
		labelModel = new DefaultListModel();
		labelList = new JList(labelModel); // Creates a new Swing list in the GUI 
		labels = new ArrayList<String>();
		init();
	}
	
	/**
	 * Initializes all the data for the panel
	 */
	private void init() {
		// Set up labelModel
		labelModel.addElement("(show all)");
		labelModel.addElement("(show unlabelled)");

		// Set up labelList
		setViewportView(labelList); // Allows for the possibility of labelList being scrollable
		labelList.getSelectionModel().setSelectionMode(
				ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); // Allows the user to select multiple rows simultaneously
		labelList.setSelectionInterval(0, labelModel.size()); // Selects all of the entries in the Swing list

		labelList.setComponentPopupMenu(new LabelPopupMenu()); // Assigns a context menu to the Swing list
		labelList.addMouseListener(new PopupListener()); // Creates and assigns a means for the context menu to be accessed via clicking
		labelList.addListSelectionListener(new SelectionListener()); // Creates and assigns a means for the GUI to see when the user changes their selection in the Swing list
		
		labelList.setSelectedIndex(0);
	}
	
	/**
	 * Getter for the labels list
	 * 
	 * @return list of labels
	 */
	public ArrayList<String> getLabels() {
		return labels;
	}
	
	/**
	 * Getter for the labels component 
	 * 
	 * @return the list compoment itself
	 */
	public JList getLabelList() {
		return labelList;
	}
	
	/**
	 * Clears the label list and refills it with current data
	 */
	public void updateLabelList() {
		// get rid of everything except "show all" and "unlabelled"
		while (labelModel.size() > 2) {
			labelModel.removeElementAt(2);
		}
		
		//add all existing labels
		for (String label : labels) {
			labelModel.addElement(label);
		}
	}

	/**
	 * An inner class to provide functionality to the popup menus
	 * 
	 * @author Jared
	 */
	class MenuItemListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand() == "new") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FilterLabelPanel_AddNewLabel());
			} else if (e.getActionCommand() == "delete") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FilterLabelPanel_DeleteLabel());
			}
		}
	}
	
	/**
	 * An inner class representing the popup menu for this panel
	 * 
	 * @author Jared
	 */
	private class LabelPopupMenu extends JPopupMenu {
		public LabelPopupMenu() {
			super();
			addNewStringItem("Add New Label...", "new");
			addNewStringItem("Delete Label", "delete");
		}

		@Override
		public void show(Component c, int x, int y) {
			int[] l = labelList.getSelectedIndices();
			super.show(c, x, y);
		}
		
		/**
		 * Adds a new item to the menu and gives it an actionlistener 
		 * 
		 * @param name		the text to display
		 * @param command	the command for the event fired
		 */
		private void addNewStringItem(String name, String command) {
			JMenuItem temp = new JMenuItem(name);
			temp.addActionListener(contextListener);
			temp.setActionCommand(command);
			add(temp);
		}
	}
	
	/**
	 * An inner class shows the popup menu when appropriate
	 * 
	 * @author Jared
	 */
	class PopupListener extends MouseAdapter {
		public void mousePressed(MouseEvent e) {
			maybeShowPopup(e);
		}

		public void mouseReleased(MouseEvent e) {
			maybeShowPopup(e);
		}

		private void maybeShowPopup(MouseEvent e) {
			if (e.isPopupTrigger()) {
				contextMenu.show(e.getComponent(), e.getX(), e.getY());
			}
		}
	}
	
	/**
	 * An inner class that keeps track of which labels are selected
	 * 
	 * @author Jared
	 */
	class SelectionListener implements ListSelectionListener {

		@Override
		public void valueChanged(ListSelectionEvent e) {
			int[] indices = labelList.getSelectedIndices();

			if (labelList.getMinSelectionIndex() == 0) {
				labelList.addSelectionInterval(1, labelModel.size());
			}

			// update the download listing panel
			boolean showUnlabelled = labelList.isSelectedIndex(1);
			ArrayList<String> argLabels = new ArrayList<String>();

			for (int i : indices) {
				if (i >= 2 && i - 2 < labels.size()) // don't delete the first
				// two, ever; also, the
				// indices array seems
				// to have extra entries
				// when everything is
				// selected
				{
					argLabels.add(labels.get(i - 2));
				}
			}
			
			//update the downloads listing based on the selected labels
			FileTransferPanel.getInstance().updateLabels(argLabels,	showUnlabelled);
		}

	}
}
